package cn.flying.cloud.base.mybatis.interceptor;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Properties;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;

import cn.flying.cloud.base.common.constants.Constants;
import cn.flying.cloud.base.common.utils.lang.SnowflakeUtil;
import cn.flying.cloud.base.common.utils.lang.ThreadLocalUtil;


/**
 * mybatis拦截器设置修改人、修改时间，设置审计相关参数
 * 同类型的拦截器，执行顺序是先加载，后执行，所有设置为100，保证尽量靠后加载
 *
 * @author: admin
 * @date: 2022年05月29日 16:50
 * @version: 1.0
 */
@Order(100)
@Component
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class})})
public class AuditInfoFillInterceptor implements Interceptor {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        fillField(invocation);
        return invocation.proceed();
    }

    private void fillField(Invocation invocation) {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        logger.info("操作类型：【{}】", sqlCommandType);
        if (sqlCommandType != SqlCommandType.INSERT && sqlCommandType != SqlCommandType.UPDATE) {
            //如果不是“增加”或“更新”操作，则退出
            return;
        }
        Object parameter = args[1];
        Object username = ThreadLocalUtil.get(Constants.USER_NAME);
        if (username == null) {
            username = "system";
        }
        if (sqlCommandType == SqlCommandType.INSERT) {
            for (Field field : parameter.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                switch (field.getName()) {
                    case "id":
                        setPropertyValue(parameter, field, SnowflakeUtil.nextId());
                        break;
                    case "createdBy":
                    case "updatedBy":
                        setPropertyValue(parameter, field, username);
                        break;
                    case "createdTime":
                    case "updatedTime":
                        Class<?> type1 = org.springframework.beans.BeanUtils.findPropertyType(field.getName(), parameter.getClass());
                        if (LocalDateTime.class.getName().equals(type1.getName())) {
                            setPropertyValue(parameter, field, LocalDateTime.now());
                        } else {
                            setPropertyValue(parameter, field, new Date());
                        }
                        break;
                    case "tenantId":
                        setPropertyValue(parameter, field, ThreadLocalUtil.get(Constants.TENANT_ID));
                        break;
                    case "revision":
                    case "deleted":
                        setPropertyValue(parameter, field, "0");
                        break;
                }
            }
        } else if (sqlCommandType == SqlCommandType.UPDATE) {
            for (Field f : parameter.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                switch (f.getName()) {
                    case "updatedBy":
                        setPropertyValue(parameter, f, username);
                        break;
                    case "updatedTime":
                        Class<?> type2 = org.springframework.beans.BeanUtils.findPropertyType(f.getName(), parameter.getClass());
                        if (LocalDateTime.class.getName().equals(type2.getName())) {
                            setPropertyValue(parameter, f, LocalDateTime.now());
                        } else {
                            setPropertyValue(parameter, f, new Date());
                        }
                        break;
                }
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
//        if (target instanceof Executor || target instanceof StatementHandler) {
//            return Plugin.wrap(target, this);
//        }
//        return target;
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }


    /**
     * 获取对象的属性赋值
     *
     * @param bean
     */
    private String getPropertyValue(Object bean, String name) {
        try {
            //根据需要，将相关属性赋上默认值
            return BeanUtils.getProperty(bean, name);
        } catch (NoSuchMethodException e) {
            logger.warn("获取属性值失败", e.getMessage());
        } catch (InvocationTargetException e) {
            logger.warn("获取属性值失败", e.getMessage());
        } catch (IllegalAccessException e) {
            logger.warn("获取属性值失败", e.getMessage());
        }
        return null;
    }

    /**
     * 为对象的操作属性赋值
     *
     * @param parameter
     * @param field
     * @param val
     */
    private void setPropertyValue(Object parameter, Field field, Object val) {
        if (StringUtils.isNotBlank(getPropertyValue(parameter, field.getName()))) {
            return;
        }
        TableId tableId = field.getAnnotation(TableId.class);
        if (tableId != null && tableId.type() == IdType.ASSIGN_ID) {
            // 检查主键策略是否为ASSIGN_ID
            return;
        }
        try {
            //根据需要，将相关属性赋上默认值
            BeanUtils.setProperty(parameter, field.getName(), val);
        } catch (IllegalAccessException e) {
            logger.warn("赋值失败", e.getMessage());
        } catch (InvocationTargetException e) {
            logger.warn("赋值失败", e.getMessage());
        }
    }
}
